/**
 * @author : River
 * @version : 1.1
 * @time : 2004.04.10
 */
package com.j3.db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;

import javax.naming.NamingException;
import javax.sql.DataSource;

import org.apache.commons.beanutils.BeanUtils;

import com.j3.string.StringUtils;

public class ConnectionManager {
	private static final String DEFAULT_VALIDATION_QUERY = "select 1";
	private static final ConnectionManager instance = new ConnectionManager();
	private final ConcurrentHashMap<String, DataSource> dataSourceTable = new ConcurrentHashMap<String, DataSource>();
	private String defaultDataSourceName = null;
	private boolean autoReconnect = true;
	private volatile boolean debug = false;

	private ConnectionManager() {
	}

	public static boolean testDataSource(Db db) throws SQLException, DataSourceUnRegisterException {
		Connection conn = getConn(db);
		return instance.testConnection(conn, db.getServerType().getValidationQuery());
	}

	private boolean testDataSource(String dataSourceName, String validationQuery) throws SQLException, DataSourceUnRegisterException {
		Connection conn = getConn(dataSourceName);
		boolean result = testConnection(conn, validationQuery);
		if (result) {
			output("Register datasource " + dataSourceName + " success!");
		} else {
			output("Register datasource " + dataSourceName + " failed!");
		}
		return result;
	}

	private boolean testConnection(Connection conn, String validationQuery) throws SQLException {
		boolean result = false;
		try {
			Statement stmt = conn.createStatement();
			ResultSet rset = null;
			if(StringUtils.isNotBlank(validationQuery)) {
				rset = stmt.executeQuery(validationQuery);
			} else {
				rset = stmt.executeQuery(DEFAULT_VALIDATION_QUERY);
			}
			if (rset.next()) {
				result = true;
			}
			rset.close();
			stmt.close();
		}finally {
			close(conn);
		}
		return result;
	}

	public static boolean registerDataSource(String dataSourceName, DataSource dataSource) throws NamingException, SQLException, DataSourceUnRegisterException {
		if (instance.dataSourceTable.containsKey(dataSourceName)) {
			return true;
		}
		instance.dataSourceTable.put(dataSourceName, dataSource);
		String validationQuery = null;
		try {
			validationQuery = BeanUtils.getProperty(dataSource, "validationQuery");
		} catch (Exception e) {
			// TODO
			e.printStackTrace();
		}
		return instance.testDataSource(dataSourceName, validationQuery);
	}

	public static Connection getConn() throws SQLException, DataSourceUnRegisterException {
		return getConn(instance.defaultDataSourceName);
	}

	public static Connection getConn(String dataSourceName) throws SQLException, DataSourceUnRegisterException {
		return instance.getConnection(dataSourceName);
	}

	private Connection getConnection(String dsName) throws SQLException, DataSourceUnRegisterException {
		String realDsName;
		if (dsName == null && defaultDataSourceName != null) {
			realDsName = defaultDataSourceName;
		} else {
			realDsName = dsName;
		}
		if (dataSourceTable == null || dataSourceTable.size() == 0) {
			throw new DataSourceUnRegisterException("No Datasource configed!");
		}
		if (realDsName == null || !dataSourceTable.containsKey(realDsName)) {
			throw new DataSourceUnRegisterException("Could not find DataSource '" + realDsName + "' !");
		}
		DataSource pool =  dataSourceTable.get(realDsName);
		Connection conn = null;
		for (int i = 0; i < 2; i++) {
			try {
				conn = pool.getConnection();
				break;
			} catch (Exception e) {
				e.printStackTrace();
				if (!autoReconnect) {
					break;
				}
			}
		}
		return conn;
	}

	public static void close(Connection conn) {
		if (conn == null)
			return;
		try {
			conn.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void output(String info) {
		if (!debug) {
			return;
		}
		System.out.println(info);
	}

	public static void clear() {
		Collection<DataSource> dataSources = instance.dataSourceTable.values();
		if(dataSources != null && !dataSources.isEmpty()) {
			instance.dataSourceTable.clear();
		}
	}

	public static String getDefaultDataSource() {
		return instance.defaultDataSourceName;
	}

	public static void setDefaultDataSourceName(String defaultDataSourceName) {
		instance.defaultDataSourceName = defaultDataSourceName;
	}

	public static void setDebug(boolean isDebug) {
		instance.debug = isDebug;
	}

	public static Connection getConn(Db db) throws DataSourceUnRegisterException, SQLException {
		if(db == null) {
			return getConn();
		}
		if(instance.dataSourceTable.containsKey(db.getServerName())) {
			return instance.getConnection(db.getServerName());
		}
		try {
			return db.getConnection();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}

}
